//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2023-10-15     ZhangYihua   first version
//
// Description  : Moving Sum 
//                sum_data[n] = cur_data[n] + cur_dat[n-1] + ... + cur_dat[n-N+1]
//                            = ACC[n] - ACC[n-N]
//                avg_data[n] = sum_data[n]*(1/N)
//################################################################################

module u_msum #(
parameter           DW                      = 16,   // data width
parameter           N_MAX                   = 8,    // N range[3, N_MAX]

// the following parameters are calculated automatically
parameter           NW                      = $clog2(N_MAX),
parameter           AW                      = $clog2(N_MAX-1)
) ( 
input                                       rst_n,
input                                       clk,
input                                       cke,

input                                       cur_vld,
input               [DW-1:0]                cur_data,
output  reg         [DW+NW-1:0]             sum_data,   // avg_data = sum_data*(1/N)

input                                       cfg_clr,
input               [DW+NW-1:0]             cfg_ini,
input               [AW-1:0]                cfg_dly     // cfg_dly=N-3 (must N>=3)
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam          ACC_BW                  = DW+NW;
localparam          DEPTH                   = 2**AW;

wire                [ACC_BW-1:0]            acc_c;
reg                 [ACC_BW-1:0]            acc;
wire                [ACC_BW-1:0]            acc_dly;
wire                [ACC_BW-1:0]            diff;

reg                                         clr_expd;
reg                 [AW-1:0]                cnt_clr;
wire                                        clr_done;

//################################################################################
// main
//################################################################################

assign acc_c = acc + cur_data;
always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        acc <=`U_DLY {ACC_BW{1'b0}};
    end else if(cke==1'b1) begin
        acc <=`U_DLY acc_c;
    end
end

dly_mem #(
        .DW                             (ACC_BW                         ),
        .DEPTH                          (DEPTH                          ),
        .MEM_STYLE                      ("TINY_MEM"                     ),	// "TPRAM"
        .EARLY_DATA                     ("ZERO"                         ),
        .ECC_MODE                       ("NONE"                         ) 	// no ECC
) u_acc_dly ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (cke                            ),

        .id                             (acc                            ),	// data before delay
        .od                             (acc_dly                        ),	// actual delay is (cfg_dly+2)*cke + (mem_rd_latency-1)

        .cfg_dly                        (cfg_dly                        )	// cfg_dly=N-3 (must N>=3)
);

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        clr_expd <=`U_DLY 1'b0;
    end else begin
        if (cfg_clr==1'b1)
            clr_expd <=`U_DLY 1'b1;
        else if (clr_done==1'b1)
            clr_expd <=`U_DLY 1'b0;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        cnt_clr <=`U_DLY {NW{1'b0}};
    end else begin
        if (cfg_clr==1'b1)
            cnt_clr <=`U_DLY cfg_dly;
        else if (clr_expd==1'b1)
            cnt_clr <=`U_DLY cnt_clr - 1'd1;
        else
            ;
    end
end
assign clr_done = (cnt_clr>cfg_dly) ? 1'b1 : 1'b0;

assign diff = acc_c - acc_dly;
always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        sum_data <=`U_DLY {ACC_BW{1'b0}};
    end else if(cke==1'b1) begin
        if (cfg_clr==1'b1 || clr_expd==1'b1)
            sum_data <=`U_DLY cfg_ini;
        else
            sum_data <=`U_DLY diff;
    end
end

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
